#!/usr/bin/env python

import os
import wx
import app
import copy
import dialog
import component

class Main(component.Presenter):
    '''Main class
    
    @author Julot <andy.julot@gmail.com>
    @since Saturday, January 14, 2012. 09:07 AM'''
    
    _hitRadius = 1
    _dragId = -1
    _image = None
    
    def __init__(self, *args, **kwargs):
        component.Presenter.__init__(self, *args, **kwargs)
        
        self.readConfig()
        
        self._pen = wx.Pen(self._penColor, 1)
        self._brush = wx.Brush("RED", wx.TRANSPARENT)
        self._cropId = wx.NewId()
        self._imageId = wx.NewId()
        
    def close(self):
        self.view.Close()
    
    _about = None
    
    def showAbout(self):
        if not self._about: self._about = dialog.About(self.view)
        self._about.Center()
        self._about.Show()
    
    def onClose(self):
        self.writeConfig()
        super(Main, self).onClose()
        
    def readConfig(self):
        config = app.Config()
        
        key = '%s/Zoom' % self.view.Name
        value = config.ReadInt(key, 10)
        self.view.ToolBar.zoom.Value = value
        
        key = '%s/Sequence' % self.view.Name
        value = config.ReadInt(key, 1)
        self.view.ToolBar.sequence.Value = value
        
        key = '%s/Format' % self.view.Name
        value = config.Read(key, 'png')
        self.view.ToolBar.format.Value = value
        
        key = '%s/Color' % self.view.Name
        value = config.Read(key, '0,0,0')
        try:
            colors = value.split(',')
            r = int(colors[0])
            g = int(colors[1])
            b = int(colors[2])
        except:
            r = g = b = 0
        self._penColor = wx.Colour(r, g, b)
        self.view.ToolBar.color.BackgroundColour = self._penColor
    
    def writeConfig(self):
        config = app.Config()
        
        key = '%s/Zoom' % self.view.Name
        config.WriteInt(key, self.view.ToolBar.zoom.Value)
        
        key = '%s/Sequence' % self.view.Name
        config.WriteInt(key, self.view.ToolBar.sequence.Value)
        
        key = '%s/Format' % self.view.Name
        config.Write(key, self.view.ToolBar.format.Value)
        
        key = '%s/Color' % self.view.Name
        color = self.view.ToolBar.color.BackgroundColour
        s = '%s,%s,%s' % (color.red, color.green, color.blue)
        config.Write(key, s)
        
    def open(self):
        wildcard = 'Image files (*.jpg; *.png)|*.jpg; *.png'
        self._path = self.view.fileDialog(wildcard=wildcard)
        if self._path:
            self._folder = os.path.dirname(self._path)
            try:
                self._image = wx.Image(self._path, wx.BITMAP_TYPE_ANY)
            except:
                self._image = None
            self.show()
    
    @property
    def zoom(self):
        return  self.view.ToolBar.zoom.Value * 10.0 / 100
    
    def show(self):
        if not self._image: return
        
        self.view.busyCursor()
        
        canvas = self.view.canvas
        canvas.pdc.RemoveAll()
        
        zoom = self.zoom
        if zoom <> 1:
            w = self._image.Width * zoom
            h = self._image.Height * zoom
            image = self._image.ResampleBicubic(w, h)
        else:
            image = self._image
        self._resizedImage = image
        canvas.Scroll(0, 0)
        canvas.maxHeight = image.Height
        canvas.maxWidth = image.Width
        
        canvas.SetVirtualSize((canvas.maxWidth, canvas.maxHeight))
        
        canvas.pdc.BeginDrawing()
        
        canvas.pdc.SetId(self._imageId)
        canvas.pdc.RemoveId(self._imageId)
        canvas.pdc.DrawBitmap(image.ConvertToBitmap(), 0, 0, False)
        w, h = image.GetSize()
        self._imageId = wx.NewId()
        canvas.pdc.SetIdBounds(self._imageId, wx.Rect(0, 0, w, h))
        
        canvas.pdc.SetId(self._cropId)
        canvas.pdc.RemoveId(self._cropId)
        canvas.pdc.SetPen(self._pen)
        canvas.pdc.SetBrush(self._brush)
        r = wx.Rect(0, 0, 300, 400)
        self.drawCrop(r)
        canvas.pdc.SetIdBounds(self._cropId, r)
        
        canvas.pdc.EndDrawing()
        
        canvas.Refresh()
        
        self.view.idle()
        
    def onMouseWheel(self, event):
        if not self._image: return
        
        pos = event.Position
        x, y = self.convertEventCoords(pos.x, pos.y)
        mouseR = wx.Rect(x, y, 1, 1)
        
        r = self.view.canvas.pdc.GetIdBounds(self._cropId)
        
        if event.controlDown:
            self.scroll(event.controlDown, event.WheelRotation < 0)
        elif r.Intersects(mouseR):
            self.resize(self._cropId, event)
        else:
            self.scroll(event.controlDown, event.WheelRotation < 0)
    
    def scroll(self, axis, forward):
        x, y = self.view.canvas.GetViewStart()
        x1, y1 = self.view.canvas.GetViewStart()
        scroll = 1 if forward else -1
        if axis: x += scroll
        else: y += scroll
        self.view.canvas.Scroll(x, y)
        
    def resize(self, target, event):
        canvas = self.view.canvas
        pdc = canvas.pdc
        
        r = pdc.GetIdBounds(target)
        x, y = self.convertCoordinate(r[0], r[1])
        
        pdc.RemoveId(target)
        
        multiplier = 2 if event.altDown else 10
        
        if event.WheelRotation > 0:
            half = int(multiplier / 2)
            r[2] += multiplier
            r[0] -= half
            old = r[3]
            r[3] = r[2] * 4 / 3
            r[1] -= int((r[3] - old) / 2)
            oldR = wx.Rect(x - half, y - half, r[2], r[3])
            oldR.Inflate(4, 4)
        else:
            oldR = wx.Rect(x, y, r[2], r[3])
            if r[2] - multiplier > 20:
                r[2] -= multiplier
                r[0] += int(multiplier / 2)
                old = r[3]
                r[3] = r[2] * 4 / 3
                r[1] += int((old - r[3]) / 2)
            
        self.drawCrop(r)
        pdc.SetIdBounds(target, r)
        canvas.RefreshRect(oldR, False)
    
    def drawCrop(self, r):
        pdc = self.view.canvas.pdc
        pdc.SetPen(self._pen)
        pdc.SetBrush(self._brush)
        pdc.DrawRectangleRect(r)
        x = r.x + r.width / 2
        y = r.y + r.height / 2
        pdc.DrawCircle(x, y, 10)
        
    def convertCoordinate(self, x, y):
        canvas = self.view.canvas
        xView, yView = canvas.GetViewStart()
        xDelta, yDelta = canvas.GetScrollPixelsPerUnit()
        return (x - (xView * xDelta),
                y - (yView * yDelta))
    
    def convertEventCoords(self, x, y):
        canvas = self.view.canvas
        xView, yView = canvas.GetViewStart()
        xDelta, yDelta = canvas.GetScrollPixelsPerUnit()
        return (x + (xView * xDelta), y + (yView * yDelta))
    
    def offsetRect(self, r):
        canvas = self.view.canvas
        xView, yView = canvas.GetViewStart()
        xDelta, yDelta = canvas.GetScrollPixelsPerUnit()
        r.OffsetXY(-(xView * xDelta), -(yView * yDelta))
        
    def onMouse(self, event):
        canvas = self.view.canvas
        hitRadius = self._hitRadius
        if event.RightDown():
            #self.lock(event)
            self.move(self._cropId, event)
        elif event.LeftDClick():
            self.crop()
        elif event.LeftDown():
            x, y = self.convertEventCoords(event.X, event.Y)
            l = canvas.pdc.FindObjects(x, y, hitRadius)
            for id in l:
                if id != self._imageId and not canvas.pdc.GetIdGreyedOut(id):
                    self._dragId = id
                    self._lastPos = (event.x, event.y)
                    break
        elif event.Dragging() or event.LeftUp():
            if self._dragId != -1:
                x, y = self._lastPos
                dx = event.x - x
                dy = event.y - y
                r = canvas.pdc.GetIdBounds(self._dragId)
                canvas.pdc.TranslateId(self._dragId, dx, dy)
                r2 = canvas.pdc.GetIdBounds(self._dragId)
                r = r.Union(r2)
                r.Inflate(4, 4)
                self.offsetRect(r)
                canvas.RefreshRect(r, False)
                self._lastPos = (event.x, event.y)
            if event.LeftUp():
                self._dragId = -1
    
    def move(self, target, event):
        canvas = self.view.canvas
        r = canvas.pdc.GetIdBounds(target)
        x, y = self.convertEventCoords(event.x, event.y)
        dx = x - r.x - (r.width / 2)
        dy = y - r.y - (r.height / 2)
        canvas.pdc.TranslateId(target, dx, dy)
        r2 = canvas.pdc.GetIdBounds(target)
        r = r.Union(r2)
        r.Inflate(4, 4)
        self.offsetRect(r)
        canvas.RefreshRect(r, False)
        
    def lock(self, event):
        canvas = self.view.canvas
        x, y = self.convertEventCoords(event.X, event.Y)
        l = canvas.pdc.FindObjects(x, y, self._hitRadius)
        if l:
            b = canvas.pdc.GetIdGreyedOut(l[0])
            canvas.pdc.SetIdGreyedOut(l[0], not b)
            r = canvas.pdc.GetIdBounds(l[0])
            r.Inflate(4,4)
            self.offsetRect(r)
            canvas.RefreshRect(r, False)
        
    def crop(self):
        if not self._image: return
        
        rectangle = self.view.canvas.pdc.GetIdBounds(self._cropId)
        b = rectangle[0] + rectangle[2] > self._resizedImage.Width or \
          rectangle[1] + rectangle[3] > self._resizedImage.Height or \
          rectangle[0] < 0 or rectangle[1] < 0
        if b:
            self.view.messageDialog('Crop target is outside image area.',
                                    'Error', wx.OK | wx.ICON_ERROR)
            return
        
        
        self.view.busyCursor()
        sequence = self.view.ToolBar.sequence
        format = self.view.ToolBar.format.Value.lower()
        filename = '%s.%s' % (sequence.Value, format)
        path = os.path.join(self._folder, filename)
        self.view.status = 'Crop and saving image as %s...' % filename
        image = self._resizedImage.GetSubImage(rectangle)
        type = wx.BITMAP_TYPE_PNG if format == 'png' else wx.BITMAP_TYPE_JPEG
        r = image.SaveFile(path, type)
        sequence.Value += 1
        self.view.idle()
        if r:
            self.view.status = 'Image successfuly saved to %s' % path
        else:
            self.view.status = ''
            wx.view.messageDialog('Error saving file.', 'Error')
        
    def changeCropColor(self):
        color = self.view.colorDialog()
        if not color: return
        
        self.view.ToolBar.color.BackgroundColour = color
        self.view.ToolBar.color.Refresh()

        self._pen = wx.Pen(color, 1)
        
        canvas = self.view.canvas
        canvas.pdc.SetId(self._cropId)
        r = canvas.pdc.GetIdBounds(self._cropId)
        canvas.pdc.RemoveId(self._cropId)
        canvas.pdc.SetPen(self._pen)
        canvas.pdc.SetBrush(self._brush)
        canvas.pdc.DrawRectangleRect(r)
        canvas.pdc.SetIdBounds(self._cropId, r)
        self.offsetRect(r)
        canvas.RefreshRect(r, False)